package cn.com.git.example.module.api;

import cn.com.git.example.common.ResultConstant;
import cn.com.git.example.common.ResultMsg;
import cn.com.git.example.common.auth.*;
import cn.com.git.example.common.base.BaseApi;
import cn.com.git.example.common.constant.LoginStateEnum;
import cn.com.git.example.common.exception.ApiException;
import cn.com.git.example.common.util.object.DateUtil;
import cn.com.git.example.common.util.object.MapperFacadeUtils;
import cn.com.git.example.common.util.security.CodecUtils;
import cn.com.git.example.common.util.web.VerifyCodeUtils;
import cn.com.git.example.common.util.web.WebUtils;
import cn.com.git.example.module.service.AuthenticationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

@Slf4j
@Api(value = "单点登录", description = "示例的描述")
@Validated
@RestController
@RequestMapping("/sso")
public class SSOApi extends BaseApi {
    @Autowired
    private AuthenticationService authenticationService;

    @GetMapping(value = "/getSession", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ApiOperation(value = "获取登录会话id", httpMethod = "GET", notes = "获取登录会话id")
    public ResultMsg<String> getSession() {
        ResultMsg resultMsg;
        try {
            String loginProcessSessionId = CodecUtils.get32UUID();
            LoginProcessDTO loginProcess = new LoginProcessDTO(loginProcessSessionId);
            loginProcess.setVerifyCode(null);
            loginProcess.setFailTimes(0);
            authenticationService.addLoginProcess(loginProcessSessionId, loginProcess);
            resultMsg = new ResultMsg<>(ResultConstant.SUCCESS, null, loginProcessSessionId);
        } catch (ApiException e) {
            e.printStackTrace();
            resultMsg = new ResultMsg<>(ResultConstant.FAILED, e.getMessage());
            log.error("获取登录会话id：{}", e.getMessage());
        } catch (Throwable e) {
            e.printStackTrace();
            resultMsg = new ResultMsg<>(ResultConstant.FAILED);
            log.error("获取登录会话id：{}", e.getMessage());
        }
        return resultMsg;
    }

    @ApiOperation(value = "登录-用户登录-密码登录", httpMethod = "POST", notes = "返回用户token", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @PostMapping(value = "/login/password", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultMsg<AuthenticationDTO> loginByPassword(@Valid @RequestBody LoginForm form, HttpServletRequest request) {
        ResultMsg<AuthenticationDTO> resultMsg = null;
        try {
            String account = form.getAccount();
            String password = form.getPassword();
            String verifyCode = form.getVerifyCode();
            String loginProcessSessionId = form.getLoginProcessSessionId();
            //登录会话验证
            LoginProcessDTO loginProcessDTO = authenticationService.findLoginProcess(loginProcessSessionId);
            if (loginProcessDTO == null) {
                return new ResultMsg<>(ResultConstant.FAILED, "无效的登录回话ID");
            }
            //验证码登录
            if (loginProcessDTO.verifyCodeEnable()) {
                resultMsg = authenticationService.loginByVerifyCode(loginProcessDTO, verifyCode, request);
                if (!resultMsg.isSuccess()) {
                    return resultMsg;
                }
            }
            //账号密码登录
            resultMsg = authenticationService.loginByPassword(account, password);
            if (resultMsg.isSuccess()) {
                //移除登录过程
                authenticationService.removeLoginProcess(loginProcessSessionId);
                ////返回参数（token） 组装
                AuthenticationDTO authenticationDTO = resultMsg.getData();
                AuthenticationTokenDTO authenticationTokenDTO = new AuthenticationTokenDTO();
                authenticationTokenDTO.setSysUser(MapperFacadeUtils.map(authenticationDTO.getSysUser(), AuthenticationTokenDTO.SysUser.class));
                String token = authenticationService.createToken(authenticationTokenDTO);
                authenticationDTO.setToken(token);
                resultMsg.setData(authenticationDTO);
                //保存登录状态
                LoginStatusDTO loginStatus = new LoginStatusDTO();
                loginStatus.setIp(WebUtils.getIp(request));
                loginStatus.setToken(token);
                loginStatus.setAccount(account);
                loginStatus.setUsername(authenticationTokenDTO.getSysUser().getName());
                loginStatus.setUserId(authenticationTokenDTO.getSysUser().getId());
                loginStatus.setLoginState(LoginStateEnum.UPLOAD_STATE_2.getValue());
                loginStatus.setLoginTime(DateUtil.date2Str(new Date(),DateUtil.DATETIMEFORMAT_STR2));
                authenticationService.modifyLoginStatus(account,loginStatus);
            } else {
                //更新登录过程
                loginProcessDTO.failTimesIncrease();
                loginProcessDTO.setLastLoginFailTime(new Date());
                loginProcessDTO.setVerifyCode(null);
                authenticationService.modifyLoginProcess(loginProcessSessionId, loginProcessDTO);
                //返回参数
                AuthenticationDTO authenticationDTO = new AuthenticationDTO();
                authenticationDTO.setLoginProcessSessionId(loginProcessSessionId);
                authenticationDTO.setVerifyCodeEnable(loginProcessDTO.verifyCodeEnable());
                authenticationDTO.setWaitSeconds(loginProcessDTO.waitSeconds());
                resultMsg.setData(authenticationDTO);
            }
        } catch (Throwable e) {
            resultMsg = new ResultMsg<>(ResultConstant.FAILED, e.getMessage());
            log.error("登录失败：{}", e.getMessage());
        }
        return resultMsg;
    }

    @GetMapping(value = "/logout", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ApiOperation(value = "登出", httpMethod = "GET", notes = "登出")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "account", value = "账号", required = true, example = "22ssdf", paramType = "query", dataType = "string"),
    })
    public ResultMsg logout(@NotNull(message = "账号不能为空") String account, HttpServletResponse response) {
        authenticationService.removeLoginStatus(account);
        return new ResultMsg(ResultConstant.SUCCESS);
    }

    @GetMapping(value = "/getVerifyCode", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ApiOperation(value = "获取验证码", httpMethod = "GET", notes = "获取验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "loginProcessSessionId", value = "登录会话ID", required = true, example = "22ssdf", paramType = "query", dataType = "string"),
    })
    public void getVerifyCode(@NotNull(message = "登录会话ID不能为空") String loginProcessSessionId, HttpServletResponse response) {
        try {
            String sessionId = loginProcessSessionId;
            // 调用工具类生成的验证码和验证码图片
            Map<String, Object> codeMap = VerifyCodeUtils.generateCodeAndPic();
            LoginProcessDTO loginProcessDTO = authenticationService.findLoginProcess(sessionId);
            loginProcessDTO.setVerifyCode(codeMap.get("code").toString());
            authenticationService.modifyLoginProcess(sessionId, loginProcessDTO);
            // 禁止图像缓存。
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", -1);
            response.setContentType("image/jpg");
            // 将图像输出到Servlet输出流中。
            ServletOutputStream sos;
            sos = response.getOutputStream();
            ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpg", sos);
            sos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
